Skip to content

Release v4.0.0#52

Merged
monstercameron merged 954 commits into
mainfrom
v4
Jun 29, 2026
Merged

Release v4.0.0#52
monstercameron merged 954 commits into
mainfrom
v4

Conversation

@monstercameron

Copy link
Copy Markdown
Owner

Ships the V4 branch to main.

Headline V4 features

  • Server functions (//gwc:server + serverfn) with gwc server gen
  • Whole-stack one-binary deploy (wholestack)
  • Query cache & mutations (query + ui.UseQuery/UseMutation)
  • Local-first CRDT sync + presence (localfirst, facepile)
  • Agent-native generative UI (agentui)
  • Time-travel snapshots (timetravel + devpanel)
  • Fine-grained signals (state.Signal), animation (anim), named slots, two-way binding, typed search params
  • New gwc commands: add, i18n gen, server gen, supplychain, vuln, buildreport, llms

Documentation (merge precursor)

  • Reference manual brought fully to the V4 spec (ch02/04/05/06/07/08/10/11/12/13)
  • api-browser (ch16): 9 new package sections + symbol rows
  • Capability matrix: 12 V4 rows wired to examples
  • llms.txt regenerated; deprecated-API content swept
  • 13 new public examples authored + wired into the generated catalog

Verification

  • Full native test suite green (134/134, GOPROXY=off)
  • All public examples build for wasm; doc gates (doclint, goblocks-compile, llms, capabilities, catalog, changelogcheck) pass

CHANGELOG Unreleased -> v4.0.0 - 2026-06-28.

🤖 Generated with Claude Code

claude and others added 30 commits June 24, 2026 16:37
…ing tests)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(ui): global-event binder no-DOM guard (v3.4.3) + hardening tests
Proves the keyed per-item components are SSR-compatible (all rows present), so a
MapKeyedComponent list can be server-rendered and hydrated. No bug found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
StyleBlock lists css.Global/css.Root registry keys (g-...) alongside hashed
classes, and Seed suppresses re-emission on hydration — so global/token rules
are not re-injected client-side (no FOUC/duplication). No bug found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…build

state/example_test.go (//go:build js && wasm) and the build-neutral
state/useatom_example_test.go both declared func ExampleUseAtom, so under the
wasm build the whole state package test binary failed to compile ("redeclared").
The wasm-only file was a redundant, never-executed (no Output:) duplicate; remove
it. The documented build-neutral example remains for all builds. Found by a
whole-module `GOOS=js GOARCH=wasm go vet` sweep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(state): wasm test-build duplicate ExampleUseAtom (v3.4.4) + SSR hardening
…r refs

WriteHTTPError is //go:build !js (native HTTP helper), but build-neutral test
files referenced it, so the diagnostics and internal/diagnostics test binaries
failed to compile under GOOS=js GOARCH=wasm ("undefined: WriteHTTPError"). Tag
the HTTP-dependent test files //go:build !js to match, and split the HTTP
benchmark into a native-only file (keeping the build-neutral NewReport
benchmark). Found by a full-module wasm `go vet` sweep; same class as the v3.4.4
state fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(diagnostics): wasm test-build broken by native-only WriteHTTPError (v3.4.5)
476k+ executions found no panic or invariant violation: non-empty input yields
non-empty output, SelectorID == "#"+escape, and colons are always escaped (G29).
The seed corpus runs on every `go test`. No bug found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hardenCSS skipped NUL inline, AFTER its lookahead guards. An input like "*\x00/"
or "<\x00/style>" therefore slipped past the guards (which saw the NUL, not the
dangerous next byte) and then had the NUL removed — silently reconstituting "*/"
or "</style>" in the emitted CSS, a style-element/comment breakout. Strip NULs in
a separate first pass so the guards run on the final byte stream.

Found by FuzzInjectHardening (fuzzing css.Inject). Re-fuzzed 1.8M+ execs with no
further bypass; regression seed committed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
security(css): fix hardenCSS NUL-byte breakout bypass (v3.4.6)
877k+ executions confirm css.Var sanitizes any input to a safe
var(--<identifier>) reference — no stray ')' / ';' / '}' / whitespace can break
out of the var() call or the declaration. No bug found.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
467k+ executions with precise oracles confirm the sanitized RawHTML path never
lets a <script>/<iframe> tag, a quoted on*= event handler, or a javascript: URL
survive into the rendered output. No bug found (the fuzz did catch and fix an
over-broad oracle in an earlier draft that flagged inert text like "onerror=0").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(css,html): fuzz Var + RawHTML sanitization (security regression coverage)
3.36M+ executions confirm the hashed-class emit path (distinct from Inject) also
applies hardenCSS — no </style>, </script>, or */ can be emitted through an
arbitrary property value, including the NUL-bypass class fixed in v3.4.6. No bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1.2M+ executions confirm markdown rendering never lets a <script>/<iframe> tag,
a quoted on*= handler, or a javascript: href/src survive into the rendered
output — covering embedded raw HTML and adversarial link/image URLs. No bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.5M+ executions confirm folding the same distinct-property rules in any order
yields the same content-hashed class and CSS — the build-reproducibility / dedup
guarantee. No bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(css,html): fuzz emit-path/markdown/determinism (hardening)
11.5M+ executions confirm UnmarshalSnapshotJSON never panics on arbitrary bytes
and that one Marshal∘Unmarshal cycle reaches a byte-stable fixpoint — the
normalizeSnapshot float64->int folding and key ordering never drift a persisted
snapshot across save/load. No bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ning

test(state): fuzz snapshot round-trip fixpoint + decode robustness
3.8M+ executions confirm RenderBootstrapScript emits exactly one <script and one
</script> regardless of attacker-controlled atoms, correlation id, route path, or
script id — embedded SSR data can never terminate the script element early. No bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(ui): fuzz SSR bootstrap script-breakout invariant
…tes (v3.4.7)

The render path emitted URL-bearing attributes (href/src/action/formaction/
poster/object data/xlink:href) verbatim, so a user-controlled URL passed through
the ordinary element API — html.A(html.Props{Href: userURL}) — could ship a
clickable <a href="javascript:alert(1)">, an XSS vector. Only markdown/RawHTML
sanitized URLs before.

Script-executing schemes (javascript:, vbscript:) are now neutralized to the
inert about:blank sentinel at BOTH the SSR serializer (internal/runtime/ssr.go:
serializeSSRAttr + writeSSRProps) and the browser DOM adapter (SetAttribute +
BatchSetAttributes), tolerating whitespace/control-char/CRLF/case obfuscation.
Safe schemes (http(s), data:, mailto, tel), relative paths, and fragments are
untouched; a URL-looking value in a non-URL attribute is left alone.

Shared SanitizeURLAttributeValue exported from internal/runtime; mockdom mirrors
it so the client path is testable natively. Vectors ported from React's
ReactDOMServerIntegrationUntrustedURL suite (TestURLSanitization*) plus
FuzzSanitizeURLAttributeValue (7M+ execs clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
security(render): block javascript:/vbscript: URLs in element attributes (v3.4.7)
Codifies the framework-agnostic subset of React's
ReactDOMServerIntegrationAttributes suite as GWC regression tests:
boolean coercion (true->bare, false->omitted), nil omission, numeric
stringification, and HTML-escaping of values. Adds:

- TestAttributeSerializationSemantics + TestAttributeNameValidationRejectsInjection
  (invalid attr names dropped, #57)
- FuzzSerializeSSRAttrBreakout (3.5M+ execs): for any name/value, an accepted
  attribute is exactly name="escaped" — two quotes, no raw <>, so a value can
  neither close the attribute early nor inject markup.

No bug found; GWC's serializer was already correct. Pure regression lock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(ui,runtime): port React attribute-serialization invariants
Ports React's escapeTextForBrowser guarantee as GWC regression tests:
&, <, >, ", ' in any text position (bare Text node, element child, nested
child, sibling children) are entity-encoded so a payload can never open a
markup tag.

- TestTextContentEscaping + TestTextContentMultipleSiblings
- FuzzTextNodeNoMarkup (4.5M+ execs): an arbitrary string rendered as a bare
  Text node never emits a raw < or >.

No bug found; GWC's text path was already sound. Pure regression lock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude and others added 28 commits June 27, 2026 21:05
Closes the last audit item: a flip-demo wasm app (keyed list + shuffle that
reorders, each item computing its FLIP invert from measured before/after rects via
anim.ComputeFLIP and recording the invert translateY in data-flip-dy) plus a
Playwright test (//go:build playwrightgo) that, in real chromium, asserts the
keyed reorder (deterministic rotate) AND a non-zero FLIP invert on moved items —
proving keyed-list FLIP works in a real browser. The data-flip-dy attribute is
persisted (not animated away), so the assertion is not timing-sensitive. Example
compiles for wasm; test compiles under -tags playwrightgo and runs in the CI
browser lane (built on the repo's proven test/playwrightgo harness).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…it FA4 niggle)

Closes the 'no combined Link* + typed-query constructor' niggle: gwc routes gen now
emits, alongside each path-param Link*, a Link*WithQuery(pathParams..., query
url.Values) that appends search params (pair with router.EncodeQuery for typed
queries) — so path and search params compose in one typed call. Regenerated the
typed-routes-demo; test: LinkUserWithQuery("42", {sort:desc}) == /users/42?sort=desc.
routesgen unit tests updated for the inline-map format + WithQuery assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…A1 niggle)

Closes the '.Text live-getter only tested under js && wasm' niggle: a native test
renders a signal's reactive text node through runtime+mockdom and asserts the
getter (with a custom formatter) produces the value — covering the .Text path off
the browser lane.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements (not documents) the FA2 ask: a native-lane DOM render test proving
UseQuery renders cached data into a real DOM through the reconciler off the browser
lane (the snapshot path), complementing the wasm SWR e2e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add NewAutoComputed, which discovers its reactive sources by running the
compute once with read-tracking on and recording every Signal.Get it
observes (Solid-style). Explicit NewComputed stays the default and
recommended path (no hidden graph); auto-tracking is an opt-in
convenience. Signal.Get gains a single-atomic-load read hook that is a
no-op outside a NewAutoComputed discovery pass. Native test proves
discovery of both sources + recompute on change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the audit checkbox: auto-tracking is no longer just documented as
a design decision — NewAutoComputed ships it as an opt-in sibling of the
default explicit NewComputed. Closes the last actionable audit item.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the six-entry stdlib deny-list with a real import-graph pass.
It seeds from explicitly client-only (js && wasm) files, follows their
imports into local module packages transitively, and classifies every
reached path: curated server stdlib, a prefix matcher for well-known
third-party server SDKs (gorm/grpc/AWS/GCP/Azure/Mongo/pgx/redis/k8s/…),
and local packages that are themselves constrained off js/wasm. Closes
the audit gap where gorm/grpc/cloud SDKs imported into a browser file —
directly or through a local helper — passed silently. Diagnostics show
the via-chain; dedup keeps one finding per (file, line, leaked path).

Source-walked (not go/packages) so the CI gate is deterministic/offline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
--fix no longer only gofmts. It now applies the deterministic structured
Edits diagnostics carry — today the server-leak fix: rewrite a browser
file's `//go:build js && wasm` (and legacy +build twin) to
`//go:build !js || !wasm`, moving the file and its server-only imports
out of the client bundle. The fixer loops to a fixed point, re-gofmts,
and reports applied vs still-manual remediations (hook-outside-component
is honestly surfaced as manual, since hoisting a hook can't be rewritten
safely). This is the AI-native post-edit loop AGENTS.md documents: an
agent that imports database/sql or gorm into a browser file runs --fix
and the file moves server-side, not just reformatted. Idempotent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add localfirst.Text, an RGA (Replicated Growable Array) for shared-doc
editing. Each character is an immutable id'd element inserted after
another; deletions are tombstones; Merge unions element sets with
deleted-wins; a deterministic id-ordered DFS from the head converges
every replica regardless of edit/merge order. Concurrent insertions —
including at the same position — both survive, closing the LWW gap the
PN-Counter (integers only) could not cover for text. Export/RestoreText
serialize the op log. Tests prove concurrent-edit convergence,
same-position survival, commutativity/idempotence, and round-trip.

Also marks Tier 0 (FB1/FB3/FC6) done in the plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove continue-on-error from the supply-chain vuln job so govulncheck
blocks merges. To keep the gate meaningful rather than perpetually red on
un-fixable toolchain CVEs, gwc vuln now classifies stdlib/toolchain
advisories: reachable DEPENDENCY advisories always block; reachable
stdlib advisories are reported but waived by default (fix = bump Go),
gateable with -include-stdlib. A reachable dependency advisory still
blocks even alongside a waived stdlib one. 3 new tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gwc doctor --fix now auto-applies the deterministic remediations doctor
would only print: the structured source edits shared with check --fix
(server-leak build-constraint rewrite) and generating a minimal
gwc-start.json for a hand-built app (main.go present, metadata absent),
never overwriting existing metadata. Non-deterministic prerequisites
(Go missing, busy port) remain reported. Adds doctor-parity.yml running
the doctor/check-fixer tests + a gwc doctor smoke on
ubuntu/macos/windows. 2 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…4, Tier2 FA5)

Router (wasm): navigation now wraps the DOM swap in the View Transitions
API by default (auto-skipped under prefers-reduced-motion; opt-out via
SetViewTransitions) and moves keyboard focus to the new route content
([autofocus]/[data-route-focus], else the container with tabindex=-1;
opt-out via SetFocusManagement). The >90% route-change case animates and
is screen-reader-correct with zero caller wiring.

gwc lint: new non-browser a11y linter (gwc-a11y) parses HTML with
x/net/html and flags missing alt, no accessible name on buttons/links,
unlabeled form controls, missing <html lang>, and positive tabindex —
no false positives on accessible markup; each finding carries the rule
as Symbol + a source line. Complements the browser-only axe gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…2 FA1)

createReactiveTextNode now takes the full source-id set and stores them
comma-joined in the atom-id prop; the reconciler splits them and wires a
fine-grained subscription per dependency. ComputedSignal.Text passes all
declared sources instead of only the first, so a multi-source computed's
text flushes when ANY dependency changes — closing the silent
missed-update footgun. Native test proves both the structural id set and
the rendered value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…6, FA2)

FA6: ui.DeferState/DeferStatus + RenderDefer (pure block router) +
UseAsyncDefer give ui.Defer Angular @defer's four blocks —
placeholder/loading/content/error — instead of a boolean latch. The
trigger runs the async loader once and routes to the matching block.
RenderDefer is unit-tested for every state and nil-block safety.

FA2: fetch.UseDurableMutation bridges the optimistic query cache and the
durable offline MutationQueue (previously disconnected). One call:
enqueue durably → optimistic cache update → background commit; success
clears the queue + commits authoritatively, failure rolls back and keeps
the entry for Replay. Lives in fetch to avoid the ui import cycle. 2
native tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gwc mcp now publishes a first-class read-only gwc_agentui_catalog tool in
tools/list, and tools/call returns the agentui allow-list (component
names + permitted prop keys) as JSON. An agent can query exactly which
components and props it may emit before generating a UI tree, turning the
allow-list from after-the-fact rejection into up-front guidance. 3 tests
cover manifest inclusion, result shape, and end-to-end JSON-RPC dispatch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add SuspenseStories (fallback-while-pending → resolved-content) and
HydrationBoundaryStories (a progressive-hydration island in every
strategy: immediate/visible/interaction/idle) plus an AllStories
aggregate. All four boundary surfaces now mount through the real
reconciler in the stories-as-tests pass, completing the workbench's
stated boundary coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… B4)

Add a table-driven completeness matrix that folds every utility category
(display/flex/spacing/sizing/color/typography/radius/effects/variants/
responsive/important) through the registry end-to-end, asserting each
emits a real class + CSS — a silently-dropped category now fails CI. Add
a no-double-emit test proving an identical utility set folds to one class
emitted exactly once. Document the Raw/Sel escape hatches as
registry-managed (content-hashed, SSR-reused), not unmanaged inline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gwc bench -fail-on-regression now exits non-zero when any benchmark
regressed beyond the 2% tolerance vs the committed baseline, turning the
existing per-benchmark comparison into a real merge gate. New
bench-drift.yml runs it on PRs at -parallel 1 for low noise. Gate logic
unit-tested for all four cases (enabled+regressed, disabled, clean, no
baseline).

Honest scope: the amd64 selector regression is not root-caused here
(this env is ARM64 Windows and cannot reproduce the amd64 numbers); the
gate is what will surface and pin it on amd64 CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The release job now cross-compiles gwc (pure-Go, CGO disabled, trimpath +
-s -w) for linux/macOS/windows × amd64/arm64, attaches them to the
GitHub release with a single SHA256SUMS manifest, and attests build
provenance for the binaries — so users skip a from-source build. All
targets verified to cross-compile cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire WAI-ARIA arrow-key roving into the tabs template — Left/Right wrap,
Up/Down, Home/End — via a pure, unit-tested tabsNextIndex helper. Add
catalog-smoke.yml: per component, scaffold a throwaway module with a
replace directive, `gwc add` it, and `go build` (real copy-into-module →
build). Add a fast offline guard that renders every catalog entry and
parses it as valid Go. Browser axe pass remains a browser-lane follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er1 F7)

Add ROADMAP.md (Now/Next/Later/Exploring themes + an Ideas-post feedback
loop), a Triage SLA table in CONTRIBUTING (security 2d / bug 5d /
feature 10d / PR 5d with escalation), a "where to ask vs. file" section,
and concrete .github/ISSUE_TEMPLATE/ (structured bug form + config that
disables blank issues and routes Q&A/ideas to Discussions and security to
the disclosure policy).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
go run ./tools/devtools-extension/pack builds
dist/gwc-devtools-<version>.zip — the directly-loadable package (Chrome/
Edge store upload as-is; Firefox Load-Temporary-Add-on; signed .xpi via
web-ext sign of the same zip). Uses Go stdlib archive/zip so it adds no
node/web-ext dependency, preserving the zero-npm posture; validates the
manifest and packages exactly the runtime files. 3 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add examples/public/third-party-js: wraps the @sindresorhus/slugify ESM
module behind a typed Go bridge (interop.ImportModule → SlugifyBridge
with Call/type-assert), with a pure-Go fallback so the same call site
works on the server. Native-stub parity test runs on the native lane
(ImportModule unavailable) and asserts the bridge is unloaded yet still
returns the correct slug via the fallback; fallback semantics are
table-tested. README documents the dynamic-import → typed-wrapper →
Go-fallback pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `gwc warm`: a long-running daemon that runs a cold build to populate
the Go build cache, then watches the project (reusing the watch
fingerprint loop) and re-runs the build on every save so the cache stays
hot — the developer's next build hits a warm cache instead of a cold
one. -target native|wasm, -once (CI), and -json (cold/warm timing report
CI can gate on). Complements gwc buildreport. 4 tests cover build args,
the wasm env, failure surfacing, and bad-target rejection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ship the V4 branch: server functions (//gwc:server + serverfn), whole-stack
one-binary deploy (wholestack), query cache/mutations (query + ui.UseQuery),
local-first CRDT sync + presence (localfirst), agent-native generative UI
(agentui), time-travel snapshots (timetravel), fine-grained signals
(state.Signal), animation engine (anim), named slots, two-way binding,
typed search params, and the gwc add/i18n gen/server gen/supplychain/vuln/
buildreport/llms commands.

Documentation brought fully to the V4 spec as a merge precursor:
- Reference manual: V4 package sections across ch02/04/05/06/07/08/10/11/12/13
- api-browser (ch16): 9 new package sections + symbol rows
- Capability matrix: 12 V4 rows, slugs wired to examples
- llms.txt/llms-full.txt regenerated
- Stale deprecated-API content swept (UseFetch/UseComputed/GetOutlet/etc.)
- 13 new public examples authored + wired into the generated catalog

CHANGELOG Unreleased -> v4.0.0. Full native test suite green (134/134).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@monstercameron monstercameron merged commit ce229b4 into main Jun 29, 2026
21 of 22 checks passed
monstercameron pushed a commit that referenced this pull request Jul 6, 2026
Two security-policy hardenings for the server-function trust boundary, both
secure-by-default and configurable.

CSRF (SetCSRFProtection, default on): Handle now requires Content-Type:
application/json and rejects browser-flagged cross-site requests. application/json
is not a CORS-safelisted content type, so a cross-origin browser call carrying it
forces a preflight an attacker's forged page cannot satisfy, and an HTML form
(limited to the three safelisted types) cannot forge a call at all. Sec-Fetch-Site:
cross-site (a JS-unspoofable, browser-set header, absent for non-browser clients)
is rejected outright as defense-in-depth. The generated client always sends
application/json, so this is transparent to generated code.

Error disclosure: a plain error's text (which can wrap a DSN/SQL/path) is no longer
returned to the caller — non-StatusError maps to a generic 500. The real error is
delivered to the new SetErrorLogger sink (default stderr) so operators keep full
diagnostics. Author-chosen messages still travel via StatusError. Read/decode
errors now use generic messages instead of leaking Go type/field names.

Pins (all negative-verified): TestHandleRejectsNonJSONContentType,
TestHandleAcceptsJSONContentTypeWithCharset, TestHandleRejectsCrossSiteFetch,
TestCSRFProtectionToggle, TestHandlePlainErrorIsGenericToClient. Updated the
relied-upon TestCallPropagatesServerError / TestStatusForErrorEdgeCases to the new
policy and regenerated the API baseline for the two new exported setters.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants